#pragma once
/*
    日志输出：
    1. 标准输出
    2. 指定文件输出
    3. 滚动输出（按照文件大小 / 按照日期）
*/
// 多种输出方式，为便于扩展，采用工厂模式；
// 抽象输出类，具体输出类；工厂构造；
#include "util.hpp"
#include <memory>
#include <sstream>
#include <fstream>
#include <cassert>

namespace logs
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {};
        virtual ~LogSink() = default;
        virtual void log(const char *data, size_t len) = 0;
    };
    // 输出方式：标准输出、指定输出、滚动输出
    class StdoutSink : public LogSink
    {
    public:
        void log(const char *data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };
    // 指定文件输出
    class FileSink : public LogSink
    {
    public:
        // 传入文件
        // /aa/bb/c.txt
        FileSink(const std::string &file_path) : _file_path(file_path)
        {
            // 创建目录
            util::File::createDirectory(util::File::getPath(file_path));
            // 创建文件
            _ofs.open(file_path, std::ios::app | std::ios::binary);
            assert(_ofs.is_open());
        }
        void log(const char *data, size_t len) override
        {
            // 将数据写入到指定文件
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        std::string _file_path;
        std::ofstream _ofs;
    };
    // 滚动输出
    class RollBySizeSink : public LogSink
    {
    public:
        // 输出的具体文件名：基础文件名 + 扩展文件名
        RollBySizeSink(const std::string &base_name, size_t max_size)
            : _base_name(base_name), _max_file_size(max_size), _cur_file_size(0), _name_count(0)
        {
            // 创建目录
            util::File::createDirectory(util::File::getPath(base_name));
            createNewFile();
        }
        // 写日志
        // 判断文件大小，超过最大大小，重新创建文件进行写入
        void log(const char *data, size_t len) override
        {
            if (_max_file_size <= _cur_file_size)
            {
                _ofs.close();
                // 获取一个新的文件名
                createNewFile();
            }
            _ofs.write(data, len);
            _cur_file_size += len;
            assert(_ofs.good());
        }

    private:
        // 自动根据基础文件名, 创建新的文件
        void createNewFile()
        {
            // 获取时间戳
            time_t t = util::Data::getTime();
            std::stringstream file_name;
            struct tm lt;
            localtime_r(&t, &lt);
            file_name << _base_name;
            // a2004-1011-13-24-16.log
            // file_name << lt.tm_year << "-";
            // file_name << lt.tm_mon << lt.tm_mday << "-";
            // file_name << lt.tm_hour << "-";
            // file_name << lt.tm_min << "-";
            // file_name << lt.tm_sec << ".log";

            // a20041011132416.log
            file_name << lt.tm_year + 1900;
            file_name << lt.tm_mon + 1;
            file_name << lt.tm_mday;
            file_name << lt.tm_hour;
            file_name << lt.tm_min;
            file_name << lt.tm_sec;
            file_name << "-" << _name_count++ << ".log";
            // 创建文件,自动生成扩展文件名
            // 格式：a.172334554.log
            _ofs.open(file_name.str(), std::ios::app | std::ios::binary);
            _cur_file_size = 0;

            assert(_ofs.is_open());
        }

    private:
        std::string _base_name;
        std::ofstream _ofs;
        size_t _max_file_size; // 记录文件最大的大小
        size_t _cur_file_size;
        size_t _name_count;
    };

    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}